{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 批归一化"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "每次在全连接层后放一个批归一化"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "一定程度上解决了梯度消失的问题，还加速了收敛速度。"
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:13.331223Z",
     "start_time": "2025-01-17T12:20:13.324270Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载数据"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:13.395368Z",
     "start_time": "2025-01-17T12:20:13.341710Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
    "# 从数据集到dataloader\n",
    "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
    "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
   ],
   "outputs": [],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:13.401132Z",
     "start_time": "2025-01-17T12:20:13.396370Z"
    }
   },
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "# print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2860], [0.3205])\n",
    ")\n"
   ],
   "outputs": [],
   "execution_count": 6
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型\n",
    "\n",
    "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
    "\n",
    "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
    "\n",
    "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
    "|-|-|-|-|-|\n",
    "|20|默认|\n",
    "|20|xaviier_uniform|\n",
    "|20|he_uniform|\n",
    "|...|\n",
    "\n",
    "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:13.567066Z",
     "start_time": "2025-01-17T12:20:13.407770Z"
    }
   },
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self, layers_num=2):\n",
    "        super().__init__()\n",
    "        self.transforms = transforms\n",
    "        self.flatten = nn.Flatten()\n",
    "        # 多加几层\n",
    "        self.linear_relu_stack = nn.Sequential(\n",
    "            nn.Linear(28 * 28, 100),  # in_features=784, out_features=300\n",
    "            # 一般而言，先batch norm 后 激活函数\n",
    "            # 参见论文 《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》\n",
    "            nn.BatchNorm1d(100),  # num of features=100\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        # 加19层\n",
    "        for i in range(1, layers_num):\n",
    "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
    "            self.linear_relu_stack.add_module(f\"batchnorm_{i}\", nn.BatchNorm1d(100)) #因为特征是1维的，所以用1维的batchnorm\n",
    "            self.linear_relu_stack.add_module(f\"relu\", nn.ReLU())\n",
    "        # 输出层\n",
    "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, nn.Linear):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # x.shape [batch size, 1, 28, 28]\n",
    "        x = self.transforms(x)\n",
    "        x = self.flatten(x)  \n",
    "        # 展平后 x.shape [batch size, 28 * 28]\n",
    "        logits = self.linear_relu_stack(x)\n",
    "        # logits.shape [batch size, 10]\n",
    "        return logits\n",
    "\n",
    "print(f\"{'layer_name':^40}\\tparamerters num\")\n",
    "total_params = 0\n",
    "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
    "    print(\"{}{:<40}\\t{:^10}\".format(idx,key, np.prod(value.shape)))\n",
    "    total_params += np.prod(value.shape)\n",
    "total_params"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "               layer_name               \tparamerters num\n",
      "0linear_relu_stack.0.weight              \t  78400   \n",
      "1linear_relu_stack.0.bias                \t   100    \n",
      "2linear_relu_stack.1.weight              \t   100    \n",
      "3linear_relu_stack.1.bias                \t   100    \n",
      "4linear_relu_stack.Linear_1.weight       \t  10000   \n",
      "5linear_relu_stack.Linear_1.bias         \t   100    \n",
      "6linear_relu_stack.batchnorm_1.weight    \t   100    \n",
      "7linear_relu_stack.batchnorm_1.bias      \t   100    \n",
      "8linear_relu_stack.Linear_2.weight       \t  10000   \n",
      "9linear_relu_stack.Linear_2.bias         \t   100    \n",
      "10linear_relu_stack.batchnorm_2.weight    \t   100    \n",
      "11linear_relu_stack.batchnorm_2.bias      \t   100    \n",
      "12linear_relu_stack.Linear_3.weight       \t  10000   \n",
      "13linear_relu_stack.Linear_3.bias         \t   100    \n",
      "14linear_relu_stack.batchnorm_3.weight    \t   100    \n",
      "15linear_relu_stack.batchnorm_3.bias      \t   100    \n",
      "16linear_relu_stack.Linear_4.weight       \t  10000   \n",
      "17linear_relu_stack.Linear_4.bias         \t   100    \n",
      "18linear_relu_stack.batchnorm_4.weight    \t   100    \n",
      "19linear_relu_stack.batchnorm_4.bias      \t   100    \n",
      "20linear_relu_stack.Linear_5.weight       \t  10000   \n",
      "21linear_relu_stack.Linear_5.bias         \t   100    \n",
      "22linear_relu_stack.batchnorm_5.weight    \t   100    \n",
      "23linear_relu_stack.batchnorm_5.bias      \t   100    \n",
      "24linear_relu_stack.Linear_6.weight       \t  10000   \n",
      "25linear_relu_stack.Linear_6.bias         \t   100    \n",
      "26linear_relu_stack.batchnorm_6.weight    \t   100    \n",
      "27linear_relu_stack.batchnorm_6.bias      \t   100    \n",
      "28linear_relu_stack.Linear_7.weight       \t  10000   \n",
      "29linear_relu_stack.Linear_7.bias         \t   100    \n",
      "30linear_relu_stack.batchnorm_7.weight    \t   100    \n",
      "31linear_relu_stack.batchnorm_7.bias      \t   100    \n",
      "32linear_relu_stack.Linear_8.weight       \t  10000   \n",
      "33linear_relu_stack.Linear_8.bias         \t   100    \n",
      "34linear_relu_stack.batchnorm_8.weight    \t   100    \n",
      "35linear_relu_stack.batchnorm_8.bias      \t   100    \n",
      "36linear_relu_stack.Linear_9.weight       \t  10000   \n",
      "37linear_relu_stack.Linear_9.bias         \t   100    \n",
      "38linear_relu_stack.batchnorm_9.weight    \t   100    \n",
      "39linear_relu_stack.batchnorm_9.bias      \t   100    \n",
      "40linear_relu_stack.Linear_10.weight      \t  10000   \n",
      "41linear_relu_stack.Linear_10.bias        \t   100    \n",
      "42linear_relu_stack.batchnorm_10.weight   \t   100    \n",
      "43linear_relu_stack.batchnorm_10.bias     \t   100    \n",
      "44linear_relu_stack.Linear_11.weight      \t  10000   \n",
      "45linear_relu_stack.Linear_11.bias        \t   100    \n",
      "46linear_relu_stack.batchnorm_11.weight   \t   100    \n",
      "47linear_relu_stack.batchnorm_11.bias     \t   100    \n",
      "48linear_relu_stack.Linear_12.weight      \t  10000   \n",
      "49linear_relu_stack.Linear_12.bias        \t   100    \n",
      "50linear_relu_stack.batchnorm_12.weight   \t   100    \n",
      "51linear_relu_stack.batchnorm_12.bias     \t   100    \n",
      "52linear_relu_stack.Linear_13.weight      \t  10000   \n",
      "53linear_relu_stack.Linear_13.bias        \t   100    \n",
      "54linear_relu_stack.batchnorm_13.weight   \t   100    \n",
      "55linear_relu_stack.batchnorm_13.bias     \t   100    \n",
      "56linear_relu_stack.Linear_14.weight      \t  10000   \n",
      "57linear_relu_stack.Linear_14.bias        \t   100    \n",
      "58linear_relu_stack.batchnorm_14.weight   \t   100    \n",
      "59linear_relu_stack.batchnorm_14.bias     \t   100    \n",
      "60linear_relu_stack.Linear_15.weight      \t  10000   \n",
      "61linear_relu_stack.Linear_15.bias        \t   100    \n",
      "62linear_relu_stack.batchnorm_15.weight   \t   100    \n",
      "63linear_relu_stack.batchnorm_15.bias     \t   100    \n",
      "64linear_relu_stack.Linear_16.weight      \t  10000   \n",
      "65linear_relu_stack.Linear_16.bias        \t   100    \n",
      "66linear_relu_stack.batchnorm_16.weight   \t   100    \n",
      "67linear_relu_stack.batchnorm_16.bias     \t   100    \n",
      "68linear_relu_stack.Linear_17.weight      \t  10000   \n",
      "69linear_relu_stack.Linear_17.bias        \t   100    \n",
      "70linear_relu_stack.batchnorm_17.weight   \t   100    \n",
      "71linear_relu_stack.batchnorm_17.bias     \t   100    \n",
      "72linear_relu_stack.Linear_18.weight      \t  10000   \n",
      "73linear_relu_stack.Linear_18.bias        \t   100    \n",
      "74linear_relu_stack.batchnorm_18.weight   \t   100    \n",
      "75linear_relu_stack.batchnorm_18.bias     \t   100    \n",
      "76linear_relu_stack.Linear_19.weight      \t  10000   \n",
      "77linear_relu_stack.Linear_19.bias        \t   100    \n",
      "78linear_relu_stack.batchnorm_19.weight   \t   100    \n",
      "79linear_relu_stack.batchnorm_19.bias     \t   100    \n",
      "80linear_relu_stack.Output Layer.weight   \t   1000   \n",
      "81linear_relu_stack.Output Layer.bias     \t    10    \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "275410"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:13.617400Z",
     "start_time": "2025-01-17T12:20:13.568074Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ],
   "outputs": [],
   "execution_count": 8
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:16.819134Z",
     "start_time": "2025-01-17T12:20:13.618405Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:16.825135Z",
     "start_time": "2025-01-17T12:20:16.820137Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.makedirs(self.save_dir) #makedirs 创建多层目录\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 10
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:16.832204Z",
     "start_time": "2025-01-17T12:20:16.825135Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:16.844766Z",
     "start_time": "2025-01-17T12:20:16.833209Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 100\n",
    "\n",
    "# 别 20 层了 2333，太深了没法训练\n",
    "model = NeuralNetwork(layers_num=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n"
   ],
   "outputs": [],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "source": [
    "loss_fct = nn.CrossEntropyLoss()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:16.848718Z",
     "start_time": "2025-01-17T12:20:16.844766Z"
    }
   },
   "outputs": [],
   "execution_count": 13
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:20:17.284322Z",
     "start_time": "2025-01-17T12:20:16.849723Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/bn\")\n",
    "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
    "# 2. save best\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/bn\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
    "\n",
    "model = model.to(device)"
   ],
   "outputs": [],
   "execution_count": 14
  },
  {
   "cell_type": "code",
   "source": [
    "\n",
    "record = training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-01-17T12:44:14.399021Z",
     "start_time": "2025-01-17T12:20:17.284322Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/375000 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "55967765b393404f8d253302de71821c"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 44 / global_step 165000\n"
     ]
    }
   ],
   "execution_count": 15
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:44:14.782345Z",
     "start_time": "2025-01-17T12:44:14.401023Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ],
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9UAAAHACAYAAACszkseAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAtRtJREFUeJzs3Qd4W+XVB/C/JMt7O3Gc4ey99wJCAhkQdgulYSRhU0ahlLbQAQTK+NgrEFYIexYohZBNCITsvXfiDO+9Lcv6nvNeXcd2PGRbsiXd/+95bjQsy/d6RDr3nPcck8PhcICIiIiIiIiIGs3c+E8hIiIiIiIiIsGgmoiIiIiIiKiJGFQTERERERERNRGDaiIiIiIiIqImYlBNRERERERE1EQMqomIiIiIiIiaiEE1ERERERERURMxqCYiIiIiIiJqogD4gIqKCpw6dQoREREwmUytvTtERGRwDocD+fn56NChA8xmnp92B77WExGRr77e+0RQLS+yiYmJrb0bRERE1Rw/fhydOnVq7d3wC3ytJyIiX32994mgWs5a6wcTGRnZrOey2WxYsmQJpk6dCqvVCn/F4/Q/RjlWHqd/8dfjzMvLUwGg/vpEzcfX+sYzynEa6Vh5nP7FKMfpz8fq6uu9TwTVehmYvMi644U2NDRUPY8//cBr4nH6H6McK4/Tv/j7cbJM2X34Wt94RjlOIx0rj9O/GOU4jXCsDb3ecyEYERERERERURMxqCYiIiIiIiJqIgbVRERERERERE3kE2uqiYh8bfxCeXk57HZ7vWuPAgICUFJSUu/jfJ0vH6fFYlH7znXTREREVB8G1UREblRWVobk5GQUFRU1GHgnJCSoTsf+HLT5+nFK05X27dsjMDCwtXeFiIiIvBSDaiIiN6moqMCRI0dUhrNDhw4qEKsrkJTHFhQUIDw8HGaz/67E8dXjlJMBcoIkPT1d/Ux79erlU/tPRERELYdBNRGRm0gQJkGkzDOUDGd95HHy+ODgYL8O1nz5OENCQtRYkGPHjlUeAxEREVFNvvUOh4jIB/ha8Eh148+SiIiIGsJ3C0RERERERERNxKCaiIiIiIiIqIkYVBMRkVt17doVL774oluea+XKlarZW05Ojluej1yzatUqXHLJJarhnnz/v/nmG5d+VsOHD0dQUBB69uyJBQsWtMi+EhERtTYG1UREhIkTJ+Lee+91y3Nt2LABt956q1uei1pHYWEhhgwZgrlz57r0eOmQftFFF2HSpEnYunWr+l26+eabsXjxYo/vKxERUWtj928iInJpxJTdbkdAQMMvG23btm2RfSLPufDCC9Xmqnnz5qFbt2547rnn1O1+/frhl19+wQsvvIBp06Z5cE+JiIhan6GC6pM5xfjDBxuRlWPB9OmtvTdEZJRgtNhmr3XUVHGZHQFl5R7rMB1itdQ5J7uq2bNn46efflLbSy+9pO579913ccMNN2DhwoX45z//iR07dmDJkiVqXNh9992HtWvXqmymBE9PPvkkJk+eXK38WzKVeuY7JiYGb7zxBn744QeVuezYsaMKvi699NImHdd//vMfPPTQQzh48CDat2+Pu+++G3/+858rP/7aa6+pYO748eOIiorCOeecgy+//FJ9TC7nzJmjPlfGng0bNgz//e9/ERYW1qR9Ic2aNWuq/Q4ICabrq34oLS1Vmy4vL09d2mw2tTWH/vnNfR5v503HuXJ/Ol5afgg2e4VHnt9sAsZEmDDFC47VW36mu07l4alF+3DT2V0xsbdvncz05O/u4l2pePPnIygt99Tvogm3ntMVFw9u71N/o560JSkHL684iL4B/vc36urPzlBBdaDFjO0n8yBvMe0VDlhbe4eIyO9JQN3/odYpgd396DSEBjb837wE0vv378fAgQPx6KOPqvt27dqlLh944AE8++yz6N69uwqOJVCdPn06Hn/8cbV29v3331drb/ft24fOnTvX+TUee+wxPP3003jmmWfwyiuv4Nprr1Xzn2NjYxt1TJs2bcLvfvc7PPLII7j66qvx66+/4o477kBcXJw6ObBx40b88Y9/xAcffIDx48cjKysLP//8s/rc5ORkzJgxQ+3HFVdcgfz8fPUxOfFBzZOSkoJ27dpVu09uS6BcXFysZn7XJCdj5ARHTXLypqE5765aunQpjMAbjvPFnRYcyW/4JF5zJGWY0X/RUgRZAKP/TOW/rZd3WXA434QNR7NwU58KDIjxvf/L3P27uyXThPf2m+FQ7/Y956nvtsN8YotP/Y16ytF84LU9FpTaTfgVZlR8ugxD4nzvd7EuRUVFLj3OUEF1TKgWRssfWk5RGRKCAlt7l4iIWp1kcwMDA1Ugk5CQoO7bu3evupQge8qUKZWPlSBY1tpWDZa//vprfPvtt7jrrrvq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bD56rLKvzclQ6xhUExERERERNYMEZjWDM7ldXl6O4OBgnwo0m8JioGOtjW8UvBMRERERERF5IQbVRERERERERE3EoJqIiIiIiIjIn9dU6wO58/Ly3LKIXlqny3P5c70/j9P/GOVYeZz+xV+PU3890l+fqPn4Wt94RjlOIx0rj9O/GOU4/flYXX2994mgOj8/X10mJia29q4QERFVe32Kiopq7d3wC3ytJyIiX329Nzl84DS7DBM/deoUIiIimj33TM42yAv28ePHERkZCX/F4/Q/RjlWHqd/8dfjlJdOeYHt0KEDzGaupHIHvtY3nlGO00jHyuP0L0Y5Tn8+Vldf730iUy0H0KlTJ7c+p/yw/ekHXhcep/8xyrHyOP2LPx4nM9Tuxdf6pjPKcRrpWHmc/sUox2nk13ueXiciIiIiIiJqIgbVRERERERERE1kuKA6KCgIDz/8sLr0ZzxO/2OUY+Vx+hejHCd5F6P83hnlOI10rDxO/2KU4zTasfpsozIiIiIiIiIib2S4TDURERERERGRuzCoJiIiIiIiImoiBtVERERERERETcSgmoiIiIiIiKiJDBVUz507F127dkVwcDDGjBmD9evXw1s8+eSTGDVqFCIiIhAfH4/LL78c+/btq/aYiRMnwmQyVdtuv/32ao9JSkrCRRddhNDQUPU8f/nLX1BeXl7tMStXrsTw4cNVd76ePXtiwYIFLfq9euSRR844jr59+1Z+vKSkBHfeeSfi4uIQHh6O3/72t0hNTfW545TnrXmcssmx+fLPc9WqVbjkkkvQoUMHtc/ffPNNtY9L78OHHnoI7du3R0hICCZPnowDBw5Ue0xWVhauvfZaREZGIjo6GjfddBMKCgqqPWb79u0455xz1D4nJibi6aefPmNfvvjiC/W7I48ZNGgQFi5c2Oh9acpx2mw2/O1vf1NfMywsTD1m5syZOHXqVIO/A0899ZRXHWdDxypmz559xnFccMEFPvczJePg633rvw7ytZ6v9b7yumCU13u+1nuYwyA+/fRTR2BgoGP+/PmOXbt2OW655RZHdHS0IzU11eENpk2b5nj33XcdO3fudGzdutUxffp0R+fOnR0FBQWVjzn33HPVficnJ1duubm5lR8vLy93DBw40DF58mTHli1bHAsXLnS0adPG8eCDD1Y+5vDhw47Q0FDHfffd59i9e7fjlVde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jhs3zt9Twn377bft1FNPtYsvvtjmzZvnLeLPPPOMFRbSuQUAgBOFXA/EA2cNEFOXX365VVZWenK98cYb7Y033rC9e/d2WP7777+3+vp6O+2006xv377+o1btQ4cOWUNDQ+RzlWRDau3++++/vTsZAAA4ccj1QDzwl2ogpnr16mWrVq2y2tpa++yzz+zVV1+1xx9/3Orq6jKWV7IsKyuzJUuWHLVMz1QBAIDcQq4H4oGbaiDG1LVr7Nix/vPkk09617AVK1Z4t67W1tZI2dGjR9vSpUvtrLPO8kFJOmvlPnjwoHcrk6+++spbus8777xujwcAAESR64HcR/dvIKbUSj1//nz79ttvfbCS5cuX2x9//GEjR460Cy64wAck2bp1q+3evdsHLrn11lttwIABPgqoBi9pbGz056tmzZplO3bsSH2uRv+866677Oeff7aPP/7YnnrqKZs5cybPWAEAcIKR64F44C/VQEypBfqLL76wV155xUf/VMv1Sy+9ZFVVVTZmzBhPovqtrmDr1q2za665xsvPmTPHBzzRCKKDBg3yZ7XSW7P1etiwYVZRUeEDoGjU0aeffrpHYwUAIInI9UA8FARBEPT0RgDIDZq7srm52T744IOe3hQAANANyPXA8UcfDwAAAAAAssRNNQAAAAAAWaL7NwAAAAAAWeIv1QAAAAAAZImbagAAAAAAssRNNQAAAAAAWeKmGgAAAACALHFTDQAAAABAlripBgAAAAAgS9xUAwAAAACQJW6qAQAAAADIEjfVAAAAAABYdv4F8Ck0FYv4IcYAAAAASUVORK5CYII="
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 16
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-17T12:44:16.771811Z",
     "start_time": "2025-01-17T12:44:14.783348Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "model=model.to(device)\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/bn/best.ckpt\",weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, val_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3166\n",
      "accuracy: 0.8886\n"
     ]
    }
   ],
   "execution_count": 17
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
